package com.atg.atgchat.init.websocket.serivce.imp;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.atg.atgchat.init.common.event.UserLoginEvent;
import com.atg.atgchat.init.user.dao.UserDao;
import com.atg.atgchat.init.user.model.entity.User;
import com.atg.atgchat.init.user.wxmp.service.LoginService;
import com.atg.atgchat.init.websocket.model.dto.WSChannelExtraRequest;
import com.atg.atgchat.init.websocket.model.enums.WebSocketResponseEnum;
import com.atg.atgchat.init.websocket.serivce.WebSocketService;
import com.atg.atgchat.init.websocket.serivce.adapter.WebSocketAdapter;
import com.atg.atgchat.init.websocket.utils.NettyUtil;
import com.atg.atgchat.init.websocket.vo.resp.WebSocketBaseResp;
import com.atg.atgchat.init.websocket.vo.resp.WebSocketLoginUrlResp;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.Date;
import javax.annotation.Resource;

import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/*
author: atg
time: 2025/1/13 13:43
*/

@Slf4j
@Component
public class WebSocketServiceImp implements WebSocketService {

    @Resource
    private UserDao userDao;
    @Resource
    @Lazy
    private WxMpService wxMpService;
    @Resource
    private LoginService loginService;
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;


    // 存储在线用户的登录状态
    private static final ConcurrentHashMap<Channel, WSChannelExtraRequest> ONLINE_USER_MAP = new ConcurrentHashMap<>();

    public static final Long MAXIMUM_SIZE = 1000L;
    public static final Duration DURATION = Duration.ofHours(1);
    //  临时存储的登录code和channel的映射
    private static final Cache<Integer, Channel> WAIT_LOGIN_MAP = Caffeine.newBuilder().
            maximumSize(MAXIMUM_SIZE).expireAfterWrite(DURATION).build();

    /**
     * 建立连接保存在容器中 hasMap存储
     *
     * @param
     */
    @Override
    public void channelConnect(Channel channel) {
        ONLINE_USER_MAP.put(channel, new WSChannelExtraRequest());
    }

    /**
     * 需要一个过期机制，还有一个淘汰策略，这个是一个map,又要淘汰机制使用本低缓存框架
     *
     * @param channel
     */
//    @SneakyThrows
    @Override
    public void handLoginRequest(Channel channel) throws WxErrorException {
        // 生成随机码
        Integer generateRandomCode = GenerateRandomCode(channel);
        // 把微信申请代参的二维码
        WxMpQrCodeTicket wxMpQrCodeTicket = wxMpService.getQrcodeService().qrCodeCreateTmpTicket(generateRandomCode, (int) DURATION.getSeconds());
        //  发送二维码--以下代码有使用适配器--WebSocketAdapter
        WebSocketBaseResp<WebSocketLoginUrlResp> webSocketBaseResp = new WebSocketBaseResp<>();
        webSocketBaseResp.setType(WebSocketResponseEnum.LOGIN_URL.getType());
        webSocketBaseResp.setData(new WebSocketLoginUrlResp(wxMpQrCodeTicket.getUrl()));
        send_Message(channel, webSocketBaseResp);
    }

    private Integer GenerateRandomCode(Channel channel) {
        //  生成随机码
        Integer code;
        do {
            code = RandomUtil.randomInt(Integer.MAX_VALUE);
        } while (Objects.nonNull(WAIT_LOGIN_MAP.asMap().putIfAbsent(code, channel)));
        return code;
    }

    @Override
    public void offLine(Channel channel) {
        ONLINE_USER_MAP.remove(channel);
        // todo 下线
    }


    @Override
    public void scanLoginSuccess(Integer eventKeyCode, Long id) {
        // 确认连接在机器上
        Channel channel = WAIT_LOGIN_MAP.getIfPresent(eventKeyCode);
        if (Objects.isNull(channel)) {
            log.warn("No channel found for eventKeyCode: {}", eventKeyCode);
            return;
        }
        User user = userDao.getById(id);
        // 剔除code
        WAIT_LOGIN_MAP.invalidate(eventKeyCode);
        // 调用登录模块获取token
        String token = loginService.login(id);
        // 登录成功响应
//        WebSocketBaseResp<WebSocketLoginSuccessResp> webSocketBaseResp = new WebSocketBaseResp<>();
//        webSocketBaseResp.setType(WebSocketResponseEnum.LOGIN_SUCCESS.getType());
//        WebSocketLoginSuccessResp successResp = WebSocketLoginSuccessResp.builder()
//                .uid(user.getId())
//                .avatar(user.getAvatar())
//                .token(token)
//                .name(user.getName())
//                .build();
//        webSocketBaseResp.setData(successResp);
//        send_Message(channel, webSocketBaseResp);
        loginSuccess(channel, user, token);
    }

    @Override
    public void sendMessageWaitAuthorization(Integer eventKeyCode) {
        Channel channel = WAIT_LOGIN_MAP.getIfPresent(eventKeyCode);
        if (Objects.isNull(channel)) {
            log.warn("No channel found for eventKeyCode: {}", eventKeyCode);
        }
        send_Message(channel, WebSocketAdapter.buildWaitAuthorizationMessage());
    }


    @Override
    public void authorize(Channel channel, String token) {
        Long validUid = loginService.getValidUid(token);
//        if (Objects.isNull(validUid)) {
//            send_Message(channel, WebSocketAdapter.buildInvalidTokenResp());
//        }
//        User user = userDao.getById(validUid);
//        // 登录和授权成功
//        loginSuccess(channel, user, token);
//        send_Message(channel, WebSocketAdapter.buildLoginResp(user, token));
        if (Objects.nonNull(validUid)) {
            User user = userDao.getById(validUid);
            loginSuccess(channel, user, token);
        }else {
            send_Message(channel, WebSocketAdapter.buildInvalidTokenResp());
        }


    }

    private void loginSuccess(Channel channel, User user, String token) {
        // 保存对应的channel
        WSChannelExtraRequest wsChannelExtraRequest = ONLINE_USER_MAP.get(channel);
        wsChannelExtraRequest.setUid(user.getId());
        //推送成功消息
        send_Message(channel, WebSocketAdapter.buildLoginResp(user, token));
        //  登录成功之后--找IP
        user.setLastOptTime(new Date());
        user.refreshIp(NettyUtil.getAttr(channel, NettyUtil.IP));
        log.info("Publishing UserLoginEvent for user: {}", user.getId());
        applicationEventPublisher.publishEvent(new UserLoginEvent(this, user));

    }


    private void send_Message(Channel channel, WebSocketBaseResp<?> webSocketBaseResp) {
        channel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(webSocketBaseResp)));
    }
}
